package com.leyou.item.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import com.leyou.common.dto.PageDTO;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.JsonUtils;
import com.leyou.item.dto.*;
import com.leyou.item.entity.*;
import com.leyou.item.mapper.SpuMapper;
import com.leyou.item.service.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class SpuServiceImpl extends ServiceImpl<SpuMapper, Spu> implements SpuService {

    @Autowired
    private BrandService brandService;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SpecGroupService specGroupService;
    @Autowired
    private SpecParamService specParamService;
    @Autowired
    private SpuDetailService spuDetailService;

    @Autowired
    private SkuService skuService;


    /**
     * todo  分页查询sku
     *
     * @param page
     * @param rows
     * @param brandId
     * @param categoryId
     * @param id
     * @param saleable
     * @return
     */
    @Override
    public PageDTO<SpuDTO> pageQuery(Integer page, Integer rows, Long brandId, Long categoryId, Long id, Boolean saleable) {

        Page<Spu> spuPage = new Page<>(page, rows);

        // select * from tb_spu [where saleable =? ,cid3 =? ,brand_id=?,id=?]
        QueryWrapper<Spu> spuQueryWrapper = new QueryWrapper<>();
        spuQueryWrapper.eq(null != saleable, "saleable", saleable)
                .eq(categoryId != null, "cid3", categoryId)
                .eq(brandId != null, "brand_id", brandId)
                .eq(id != null, "id", id);

        this.page(spuPage, spuQueryWrapper);

        //获取分页查询出来的数据集合
        List<SpuDTO> spuDTOS = SpuDTO.convertEntityList(spuPage.getRecords());

        for (SpuDTO spuDTO : spuDTOS) {//不需要返回用for循环 或者forEach
            //根据分类id的集合查询对应的分类集合 并且取出分类names 拼接为字符串
            List<CategoryDTO> categoryDTOS = this.categoryService.listCategoryByIds(spuDTO.getCategoryIds());
            String names = categoryDTOS
                    .stream()
                    .map(CategoryDTO::getName)
                    .collect(Collectors.joining("/"));//对查询出来的分类名字进行加/拼接
            //把查询出来的分类名字(用/连接的) 赋值给spuDTO的CategoryName,
            spuDTO.setCategoryName(names);

            //根据brand的id查询对应的品牌  赋值给要返回的值brandName中
            spuDTO.setBrandName(brandService.getById(spuDTO.getBrandId()).getName());
        }

        //todo 查询要考虑缓存 缓存击穿

        //封装spu的分页查询结果
        PageDTO<SpuDTO> spuDTOPageDTO = new PageDTO<>(spuPage.getTotal(), spuPage.getPages(), spuDTOS);
        return spuDTOPageDTO;
    }


    //TODO 根据商品id(spuId) 查询对应得规格参数组集合 以及组内参数信息 及对应值
    @Override
    public List<SpecParamDTO> listSpecByspuId(Long spuId, Boolean searching) {

        Long cid3 = this.getById(spuId).getCid3();//获得分类的id

      /*  //根据category_id 查询出 规格参数分组的集合
        List<SpecGroup> specGroups = this.specGroupService.query().eq("category_id", cid3).list();
        List<SpecGroupDTO> specGroupDTOS = SpecGroupDTO.convertEntityList(specGroups);//转化为Dto,会加上 list params*/

       /* for (SpecGroupDTO specGroupDTO : specGroupDTOS) {
            List<SpecParam> specParams = specParamService.query().eq("group_id", specGroupDTO.getId()).list();
            List<SpecParamDTO> specParamDTOS = SpecParamDTO.convertEntityList(specParams);


            specGroupDTO.setParams(specParamDTOS);//对DTO的List<SpecParamDTO> params进行赋值 然后返回
        }*/

        //根据分类id 查询当前分类下所有的规格参数
        List<SpecParam> specParams = this.specParamService
                .query()
                .eq("category_id", cid3)
                .eq(null!=searching,"searching",searching)//动态查询
                .list();
        List<SpecParamDTO> specParamDTOS = SpecParamDTO.convertEntityList(specParams);


        //对集合参数集合, 进行分组, 以 GroupId分组,同id的为一组, key为gorupId value为 同id的List<SpecParam>
 /*       Map<Long, List<SpecParamDTO>> specMap = specParamDTOS
                .stream()
                .collect(Collectors.groupingBy(SpecParamDTO::getGroupId));
*/
        //处理每一个specParam的 Value,在spuDetail中  spuId是前端传来的商品id
        SpuDetail spuDetail = this.spuDetailService.getById(spuId);


        //JsonUtils.toMap("json",Long.class,Object.class);限定key 为long ,velue为Object,如双层map 就不好用了
        //把json 转化为 map ;nativeRead 第一个参数 josn,第二个参数,类型限定
        //map中 key long是规格参数id Object value是规格参数的值
        Map<Long, Object> valueMap = JsonUtils.nativeRead(spuDetail.getSpecification(),
                new TypeReference<Map<Long, Object>>() {
                });

        //遍历specParamDTOS 给每个value赋值,每个value的值,就是valueMap的value
        for (SpecParamDTO specParamDTO : specParamDTOS) {
            //规格参数的id
            Long key = specParamDTO.getId();
            //Object o =valueMap.get(key) ;
            specParamDTO.setValue(valueMap.get(key));
        }

    /*    //规格参数集合  存入到规格参数组中
        for (SpecGroupDTO specGroupDTO : specGroupDTOS) {
            specGroupDTO.setParams(specMap.get(specGroupDTO.getId()));
        }*/

        return specParamDTOS;
    }

    //todo 新增商品
    @Override
    @Transactional
    public void addGoods(SpuDTO spuDTO) {

        if (CollectionUtils.isEmpty(spuDTO.getSkus())) {

            throw new LyException(500, "每个spu 必须要有一个sku");
        }

        Spu spu = spuDTO.toEntity(Spu.class);
        spu.setSaleable(false);
        this.save(spu);//添加Spu 同时主键回显

        //新增 SpuDetail
        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();
        SpuDetail spuDetail = spuDetailDTO.toEntity(SpuDetail.class);

        spuDetail.setSpuId(spu.getId());//把spu的id传给spuDetail

        spuDetailService.save(spuDetail);//添加商品的详情

        //3.新增skus
        List<Sku> skulist = new ArrayList<>();
        //获取skus
        List<SkuDTO> skus = spuDTO.getSkus();
        for (SkuDTO skuDTO : skus) {//遍历skus,并且获得sku,对每个sku的"spu_id"进行赋值
            Sku sku = skuDTO.toEntity(Sku.class);
            sku.setSaleable(false);
            sku.setSpuId(spu.getId());
            skulist.add(sku);
        }

        /*List<Sku> skuList = skus.stream().map(skuDTO -> {
            Sku sku = skuDTO.toEntity(Sku.class);
            sku.setSpuId(spu.getId());
            sku.setSaleable(false);
            return sku;
        }).collect(Collectors.toList());//此步骤相当于上面的
*/

        skuService.saveBatch(skulist);//批量保存



    }

    @Autowired
    public AmqpTemplate amqpTemplate;


    //todo 商品上下架更改
    @Transactional
    @Override
    public void updateSaleable(Long id, Boolean saleable) {

        Spu spu = new Spu();
        spu.setId(id);
        spu.setSaleable(saleable);
        spu.setUpdateTime(new Date());
        boolean b = this.updateById(spu);
        if (!b) {
            throw new LyException(500, "更新失败");
        }

        //商品spu 下架了 它关联的sku 也得下架

        boolean update = skuService.update().eq("spu_id", id).set("saleable", saleable).update();

        if (!update) {
            throw new LyException(500, "更新失败");
        }

        // mq  通过mq来获取商品上下架的信息
        //如果saleable为ture 为item.up
        String routingKey = saleable ? "item.up":"item.down";
        //向指定的交换机发送消息,消息内容为 spuId
        this.amqpTemplate.convertAndSend("leyoujhj",routingKey,id);
         //todo                               交换机   路由key  传的信息
    }

    //todo 根据商品id 查询商品 3表
    @Override
    public SpuDTO queryGoodsById(Long id) {

    /*    //复用上面的分页查询出来的信息
        List<SpuDTO> spuDTOS = this.pageQuery(1, 1, null, null, id, null)
                .getItems();//可以获得一个spuDTO
        if (CollectionUtils.isEmpty(spuDTOS)){
            throw new LyException(204,"根据商品id没有查到 商品");
        }

        SpuDTO spuDTO = spuDTOS.get(0);  //todo 用此方法的话最下面的 获得商品分类的名称 和品牌的名称就不用写了,因为分页查询已经获得了


        */
        //查询spu表
        Spu spu = this.getById(id);
        SpuDTO spuDTO = new SpuDTO(spu);//转化为dto

        //查询spuDetail 并且赋值进 spuDTO
        SpuDetail spuDetail = spuDetailService.getById(id);
        SpuDetailDTO spuDetailDTO = new SpuDetailDTO(spuDetail);//转化为Dto
        spuDTO.setSpuDetail(spuDetailDTO);

        //查询sku  1个spuid对象N 个skus  todo 并且赋值进 spuDTO
        List<SkuDTO> skuDTOS = skuService.querySkusBySpuId(id);//todo 复用原来的代码
       /* List<Sku> skuList = skuService.query().eq("spu_id", id).list();
        List<SkuDTO> skuDTOS = SkuDTO.convertEntityList(skuList);  //被优化了*/
        spuDTO.setSkus(skuDTOS);

        //todo 获得商品分类的名称 和品牌的名称
        Long brandId = spuDTO.getBrandId();
        Brand brand = brandService.getById(brandId);
        spuDTO.setBrandName(brand.getName());
        //获得商品分类名称
        List<Category> categories = categoryService.listByIds(spuDTO.getCategoryIds());
        String names = categories.stream().map(Category::getName).collect(Collectors.joining("/"));

       /* String s ="";
        for (Category category : categories) {
            s+=category.getName()+"/";
        }*/

        spuDTO.setCategoryName(names);


        return spuDTO;
    }

    //todo 更新商品
    @Override
    @Transactional
    public void updateSpu(SpuDTO spuDTO) {

        if (null != spuDTO.getId()) {
            Spu spu = spuDTO.toEntity(Spu.class);
            //修改spu

            this.updateById(spu);//更改spu
        }
        //修改spuDetail
        SpuDetailDTO spuDetailDTO = spuDTO.getSpuDetail();

        if (spuDetailDTO != null && spuDetailDTO.getSpuId() != null) {//更新是根据id的有无进行更改的
            SpuDetail spuDetail1 = spuDetailDTO.toEntity(SpuDetail.class);
            spuDetailService.updateById(spuDetail1);//更改spuDetail
        }
        //修改sku  先进行前端传值判断 如果 没有进行sku的操作 直接返回
        List<SkuDTO> skuDTOs = spuDTO.getSkus();
        if (null == skuDTOs) {
           // new LyException(500,"每个spu必须要有一个sku");
           // return;
        }
        // SkuDTO.convertEntityList(skuDTOs);

        //判断sku是否有saleable 有的话则删除sku 没有则是增加或者改sku 有id 改 没有id 删除

        for (SkuDTO skuDTO : skuDTOs) {
            Sku sku = skuDTO.toEntity(Sku.class);
            Boolean saleable = sku.getSaleable();

            /*if (saleable == null) {//如果saleable为false 删除sku
                //skuService.removeById(sku.getId());
                skuService.saveOrUpdate(sku);
            }*/

            if (saleable != null) {
                //skuService.saveOrUpdate(sku);
                skuService.removeById(sku.getId());

                /*if (sku==null){
                    new LyException(500,"每个spu必须要有一个sku");
                }*/

            }
            if (saleable == null) {//如果saleable为false 删除sku
                //skuService.removeById(sku.getId());
                skuService.saveOrUpdate(sku);
            }




        }


    }

    //todo 根据spu的Id查询spu，不包含其它信息
    @Override
    public SpuDTO querySpuByid(Long id) {

        Spu spu = this.getById(id);

        return new SpuDTO(spu);
    }


}
